package com.github.beihaifeiwu.myspring.command;

import com.github.beihaifeiwu.myspring.core.MySpringConstants;
import com.github.beihaifeiwu.myspring.core.SpringBeans;
import com.github.beihaifeiwu.myspring.utils.EX;
import com.github.beihaifeiwu.myspring.utils.Lazy;
import com.google.common.base.Preconditions;
import lombok.experimental.UtilityClass;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * Created by liupin on 2016/11/10.
 */
@UtilityClass
public class CMD {

    private static Lazy<CommandDispatcher> dispatcher = Lazy.of(() -> {
        return SpringBeans.getBean(CommandDispatcher.class)
                .orElseThrow(() -> EX.wrap("Please check your configuration: %s.enabled=true", MySpringConstants.COMMAND_PROP_PREFIX));
    });

    @Nullable
    public static CommandMethod<?, ?> getCommand(String category, String action) {
        return dispatcher.get().getCommand(category, action);
    }

    /**
     * cmd details
     */
    public static List<String> categories() {
        return dispatcher.get().getCommands()
                .stream()
                .map(CommandMethod::getCategory)
                .collect(Collectors.toList());
    }

    public static List<String> actions(String category) {
        return dispatcher.get().getCommands(category)
                .stream()
                .map(CommandMethod::getAction)
                .collect(Collectors.toList());
    }

    /**
     * cmd help info
     */
    public static String help(String category, String action) {
        return getCommand(category, action).toString();
    }

    public static List<String> help(String category) {
        List<CommandMethod<?, ?>> commands = dispatcher.get().getCommands(category);
        if (CollectionUtils.isEmpty(commands)) {
            return Collections.emptyList();
        }
        return commands.stream().map(CommandMethod::toString).collect(Collectors.toList());
    }

    public static List<String> help() {
        List<CommandMethod<?, ?>> commands = dispatcher.get().getCommands();
        if (CollectionUtils.isEmpty(commands)) {
            return Collections.emptyList();
        }
        return commands.stream().map(CommandMethod::toString).collect(Collectors.toList());
    }

    /**
     * cmd execute ways
     */
    @Nullable
    public static Object sync(String category, String action, Object... args) {
        return dispatcher.get().execute(category, action, args);
    }

    @Nullable
    public static CompletableFuture<?> async(String category, String action, Object... args) {
        return dispatcher.get().submit(category, action, args);
    }

    @Nullable
    public static Object eval(@Nullable String... args) {
        Preconditions.checkArgument(args != null && args.length > 0,
                "args should at least includes action");
        String category = args[0];
        String action = args.length > 1 ? args[1] : null;
        String[] params;
        CommandMethod<?, ?> command = getCommand(category, action);
        if (command != null) {
            params = Arrays.copyOfRange(args, 2, args.length);
        } else {
            command = getCommand(MySpringConstants.DEFAULT_PROP_PREFIX, category);
            Preconditions.checkNotNull(command, "No command found for (%s, %s) or (%s, %s)",
                    category, action, MySpringConstants.DEFAULT_PROP_PREFIX, action);
            params = args.length > 1 ? Arrays.copyOfRange(args, 1, args.length) : null;
        }
        try {
            return command.invoke(params);
        } catch (Exception e) {
            throw EX.wrap(e, "Cannot execute command %s", command);
        }
    }
}
